在工作过程中,部分项目收到客户反馈,出现偶发性Crash,为了尽可能的复现客户问题,需要在程序内模拟用户输入,比如拍照,切换模式,切换分辨率,开关HDR模式等,之前有大概介绍过,目前在Android上做这类UI测试的几种主要方式:
- 利用espresso,uiautomator等UI测试框架去寻找特定的View,并执行对应操作。优点是:应用场景全面,包括普通的点击滑动,还有自动输入等高级操作;大概率可以忽略屏幕适配的问题;可以在user版本运行。缺点:学习成本较高;复杂场景脚本维护工作了较大。
- 利用adb shell input x y 的方式去模拟用户点击,组合操作可以通过脚本的方式完成。优点:方便实用;快。缺点:复杂场景脚本的设计会比较艰难,需要连接PC。
- 因为Android也是类Linux系统,可以模拟鼠标等驱动事件,直接往Linux底层写事件。优点:可以进行事件等录制,适应场景极多;快速。缺点:学习成本较高。
考虑到我们的场景,以及后面还有类似的项目问题,所以我们选择了第三种,可以录制用户操作,然后按照录制的操作开始循环执行的方式。
1.原理分析
1.1.Android界面点击事件流程
用户在屏幕上点击一下后,程序里面的OnClickListener是怎样收到这个事件的。大致流程如下 :
用户点击 -> (硬件驱动部分)硬件产生一个中断 -> /dev/input/event* 写入一个相应的信号 -> jni部分 android循环读取/dev/input/event的事件 -> 分发给WindowManagerServer -> 最后再发到相应的ViewGroup和View。
这里我们就可以通过往/dev/input/event* 写信号的方式,来达到模拟事件的目的。
1.2.触摸协议分析
在2,1中,我们知道可以通过模拟的方式来达到我们模拟事件的目的,但是有个问题在于,事件的种类分为很多种,比如物理按键事件,触摸屏事件,蓝牙等,此处我们重点分析触摸屏事件。
触摸协议分单点触摸和多点触摸。以单点触摸为例,打开手机,同样关闭自动旋屏,输入adb shell getevent,鼠标点击一下屏幕,要足够快,不然数据太多。得到输出和下面类似。
1 | [min@bogon:] ~ $ adb shell getevent |
可单点触摸的协议每次点击会写6条信号,按照这个的格式:type code value,具体type含义,在本段末尾有贴出,结合type说明分析单点触摸事件如下:
1 | /dev/input/event0: 0003 0000 00000117 EV_ABS ABS_X 0x117 |
触摸点的x坐标
1 | /dev/input/event0: 0003 0001 0000020f EV_ABS ABS_Y 0x20f |
触摸点的y坐标
1 | /dev/input/event0: 0001 014a 00000001 EV_KEY BTN_TOUCH 1 |
touch down
1 | /dev/input/event0: 0000 0000 00000000 EV_SYN 0 0 |
同步信号量
1 | /dev/input/event0: 0001 014a 00000000 EV_KEY BTN_TOUCH 0 |
touch up
1 | /dev/input/event0: 0000 0000 00000000 EV_SYN 0 0 |
同步信号量
1 | 其中,type定义如下: |
1.3.可行性验证
那么具体等事件分析已经结束,我们是否可以将这些事件写入对应的/dev/input/event* 来达到我们模拟事件点击的目的呢?进入开发者模式,打开show touches 和pointer locations勾上后,可以看到点击的轨迹,再结合adb shell sendevent输入以上消息,可以看到屏幕上出现点击效果。
实践证明我们是可以模拟的点击事件的,而且已经有现成的getevent 和 sendevent的方式可以很方便的使用。但是实际上,当我们使用这样的方式在Lenovo,Vivo的部分机型上验证的时候发现,getevent得到响应的事件输出的速度让人捉急,这中间产生的耗时对我们的UI测试来说其实很难忍受。
2.实践改造
基于getevent和setevent的原理,源码路径, 我们实现了一套可以从/dev/input/event*读取并写入的Kit,有兴趣的可以邮件联系我获得源码
备注:目前已经在MI 6,Lenovo zap,Vivo X21,HW nova4上验证通过。